use crate::{
    csrc, csrci, csrr, csrs, csrsi, csrw,
    interrupt::Code::{
        self, MachineExternal, MachineSoftware, MachineTimer, SupervisorExternal,
        SupervisorSoftware, SupervisorTimer,
    },
};
use bits::{BitsOps, IntoBits};
use core::marker::PhantomData;

pub struct Cache {
    /// supervisor-level external interrupts
    pub ssie: bool,
    /// macine-level 软中断使能
    pub msie: bool,
    pub stie: bool,
    /// machine 定时器中断
    pub mtie: bool,
    pub seie: bool,
    /// 中断使能位
    pub meie: bool,
    phantom: PhantomData<u32>,
}
impl From<usize> for Cache {
    fn from(raw: usize) -> Self {
        Cache {
            ssie: raw.bits(SupervisorSoftware as u32).is_set(),
            msie: raw.bits(MachineSoftware as u32).is_set(),
            stie: raw.bits(SupervisorTimer as u32).is_set(),
            mtie: raw.bits(MachineTimer as u32).is_set(),
            seie: raw.bits(SupervisorExternal as u32).is_set(),
            meie: raw.bits(MachineExternal as u32).is_set(),
            phantom: PhantomData,
        }
    }
}
impl Into<usize> for Cache {
    fn into(self) -> usize {
        let ret = 0usize;
        ret.bits(MachineSoftware as u32)
            .write(self.ssie as usize)
            .bits(MachineSoftware as u32)
            .write(self.msie as usize)
            .bits(SupervisorTimer as u32)
            .write(self.stie as usize)
            .bits(MachineTimer as u32)
            .write(self.mtie as usize)
            .bits(SupervisorExternal as u32)
            .write(self.seie as usize)
            .bits(MachineExternal as u32)
            .write(self.meie as usize)
    }
}
impl Cache {
    pub fn flush(self) {
        let val: usize = self.into();
        csrw!(mie, val);
    }
}

pub fn cache() -> Cache {
    let raw = csrr!(mie);
    raw.into()
}
pub fn ssie(enable: bool) {
    if enable {
        csrsi!(mie, 0b10);
    } else {
        csrci!(mie, 0b10);
    }
}
pub fn msie(enable: bool) {
    if enable {
        csrsi!(mie, 0b1000);
    } else {
        csrci!(mie, 0b1000);
    }
}
fn cfg_by_pos(enable: bool, pos: Code) {
    let mask = 1usize << (pos as usize);
    if enable {
        csrs!(mie, mask);
    } else {
        csrc!(mie, mask);
    }
}
pub fn stie(enable: bool) {
    cfg_by_pos(enable, SupervisorTimer);
}
pub fn mtie(enable: bool) {
    cfg_by_pos(enable, MachineTimer);
}
pub fn seie(enable: bool) {
    cfg_by_pos(enable, SupervisorExternal);
}
/// 配置中断使能
pub fn meie(enable: bool) {
    cfg_by_pos(enable, MachineExternal);
}
